﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.IO;

namespace GT.Algorithms
{
	public class GST
	{
		public static event EventHandler<NewEventArgs> NewGST;
		public static event EventHandler<ReportEventArgs> Report;
		#region Constants
		public const int MIN_LENGTH = 10;
		public const int MIN_DEPTH = 1;
		public const int MAX_DEPTH = 10;
		public const string ROOT_ID = "AF810092-98D2-41FA-AA0B-2C5804474B6E";
		public const string SUFFIX_MARK = "$";
		public const string CORPUS_PATH = "\\Corpus\\";
		public static readonly string[] STOPWORDS = new string[] { "các ", "cho ", "và ", "của ", "chứa ", "trong ", "là " };
		#endregion
		#region Properties
		public Node Root { get; private set; }
		public bool IsSaving { get; set; }
		public static bool IsReport { get; set; }
		#endregion
		#region Constructors
		public GST()
		{
			Root = new Node();
			Root.NodeID = new Guid(ROOT_ID);
			Root.NodeText = "/";
			Root.Text = "/";
			Root.Depth = 0;
			Root.Node0 = new Node();
			Root.Node0.Parent = Root;
			Root.Node0.NodeText = SUFFIX_MARK;
			Root.Node0.Text = SUFFIX_MARK;
			Root.Node0.Depth = 1;
		}
		#endregion
		#region Methods
        public static string Preprocess(string content)
        {
			content = content.ToLower();
			for (int i = 0; i < STOPWORDS.Length; i++)
			{
				content = content.Replace(STOPWORDS[i], " ");
			}
            content = Regex.Replace(content, @"[^\w\.!?]", " ");
            content = Regex.Replace(content, @"[.]+", ".");
            content = Regex.Replace(content, @"[\s]+", " ");
            return content;
        }
		public Node Search(string text)
		{
			string[] words = text.Split(' ');

			return null;
		}
		public void Add(string sentence)
		{
			if (string.IsNullOrEmpty(sentence) || sentence.Length < MIN_LENGTH) return;
			string[] words = sentence.Trim().Split(' ');
			for (int j = 0; j < words.Length; j++)
			{
				Node s = null;
				for (int k = 0; k <= j; k++)
				{
					bool found = false;
					Node x = Search(words, k, j, out found);
					Node t = null;
					if (x.NodeText == SUFFIX_MARK)
					{
						if (x.Parent == Root || (x.Next == null && !found))
						{
							t = new Node(x, words[j]);
						}
						else
						{
							t = x;
						}
					}
					else
					{
						t = new Node(x, words[j]);
					}
					if (s == null)
					{
						s = t;
					}
					else
					{
						s.Link = t;
						if (s.Node0 != null)
						{
							s.Node0.Link = (t.Node0 != null) ? t.Node0 : null;
						}
						s = t;
					}
					Node xx = s;
					while (xx != null && xx != this.Root && j == words.Length - 1)
					{
						xx.TF++;
						if (xx.Node0 != null) xx.Node0.TF++;
						xx = xx.Parent;
					}
				}
			}
		}
		private Node Search(string[] words, int k, int j, out bool found)
		{
			Node n = Root.Node0;
			bool stop = n.Next == null;
			while (!stop && k <= j)
			{				
				if (n.NodeText == words[k])
				{
					k++;
					n = n.Node0;
				}
				else
				{
					stop = n.Next == null;
					if (n.Next != null)
					{
						n = n.Next;
					}
				}
			}
			found = k > j;
			return n;
		}
		public List<Node> FindFWS(int t)
		{
			List<Node> nodes = new List<Node>();
			Queue<Node> orders = new Queue<Node>();
			orders.Enqueue(Root);
			while (orders.Count != 0)
			{
				Node n = orders.Dequeue();
				Node x = null;
				if (n.Depth > MIN_DEPTH && n.Depth < MAX_DEPTH && n.TF > t)
				{
					nodes.Add(n);
					x = n.Parent;
					while (x != null)
					{
						nodes.Remove(x);
						if (x.Parent != null && x.Parent.Node0 != null) nodes.Remove(x.Parent.Node0);
						x = x.Parent;
					}
					x = n.Link;
					while (x != null)
					{
						nodes.Remove(x);
						x = x.Link;
					}
				}
				
				if (n.Node0 != null)
				{
					orders.Enqueue(n.Node0);
					x = n.Node0.Next;
					while (x != null)
					{
						orders.Enqueue(x);
						x = x.Next;
					}
				}
			}
			return nodes;
		}

        public static void New(string content)
        {
            new Thread(new ThreadStart(() =>
            {
                string[] sentences = content.Split(new char[] { '.', '!', '?' });
                GST gst = new GST();
				if (IsReport) Report(null, new ReportEventArgs(string.Format("Đang tạo cây hậu tố... {0}%", 0), 0));
                for (int i = 0; i < sentences.Length; i++)
                {
                    gst.Add(sentences[i]);
					Report(null, new ReportEventArgs(string.Format("Đang tạo cây hậu tố... {0}%", (i + 1) * 100 / sentences.Length), (i + 1) * 100 / sentences.Length));
                }
                NewGST(null, new NewEventArgs() { New = gst });
				if (IsReport) Report(null, new ReportEventArgs(string.Format("Đang tạo cây hậu tố... {0}%", 100), 100));
            })).Start();
        }
		public void Save(string path)
		{
			if (IsSaving) return;
			new Thread(new ThreadStart(() =>
			{
				this.IsSaving = true;
				Queue<Node> nodes = new Queue<Node>();
				nodes.Enqueue(this.Root);
				using (StreamWriter sw = File.CreateText(path))
				{
					while (nodes.Count > 0)
					{
						Node x = nodes.Dequeue();
						if (x.NodeText != SUFFIX_MARK)
						{
							sw.WriteLine(string.Format("{0} {1} {2}", x.NodeID, x.NodeText, (x.Parent != null) ? x.Parent.NodeID : Root.NodeID));
						}
						if (x.Node0 != null)
						{
							nodes.Enqueue(x.Node0);
							Node n2 = x.Node0.Next;
							while (n2 != null)
							{
								nodes.Enqueue(n2);
								n2 = n2.Next;
							}
						}
					}
				}
				
// 				GT.DataAccess.Node n = GT.DataAccess.Node.Get(new Guid(ROOT_ID));
// 				this.Root.Tag = n;
// 				Queue<Node> nodes = new Queue<Node>();
// 				nodes.Enqueue(this.Root);
// 				while (nodes.Count > 0)
// 				{
// 					Node x = nodes.Dequeue();
// 					if (x.Depth > 0)
// 					{
// 						GT.DataAccess.Node n1 = GT.DataAccess.Node.Get(x.NodeText, (x.Parent.Tag as GT.DataAccess.Node).NodeID);
// 						if (n1 == null)
// 						{
// 							n1 = new GT.DataAccess.Node(Guid.NewGuid(), x.NodeText, 1, x.Parent.Tag as GT.DataAccess.Node);
// 							n1.Insert(false);
// 						}
// 						else
// 						{
// 							n1.NumDocument++;
// 							//n1.Update();
// 						}
// 						x.Tag = n1;
// 					}
// 					if (x.Node0 != null)
// 					{
// 						nodes.Enqueue(x.Node0);
// 						Node n2 = x.Node0.Next;
// 						while (n2 != null)
// 						{
// 							nodes.Enqueue(n2);
// 							n2 = n2.Next;
// 						}
// 					}
// 				}
// 				GT.DataAccess.GTDataContext.Context.SubmitChanges();
				this.IsSaving = false;
				Report(this, new ReportEventArgs(string.Format("Hoàn thành lưu dữ liệu... {0}%", 100), 100));
			})).Start(); ;
		}
        #endregion
	}
    public class NewEventArgs : EventArgs
    {
        public GST New { get; set; }
    }
	public class Node
	{
		#region Properties
		public Guid NodeID { get; set; }
		public string NodeText { get; set; }
		public string Text { get; set; }
		public int Depth { get; set; }
		public int TF { get; set; }
		public int NumDocs { get; set; }
		public double IDF { get; set; }
		public double TFIDF { get; set; }
		public double WFIDF { get; set; }
		public double NTFIDF { get; set; }
        public double Weight { get; set; }
		public Node Node0 { get; set; }
		public Node Previous { get; private set; }
		public Node Next { get; set; }
		public Node Link { get; set; }
		public Node Parent { get; set; }
        public object Tag { get; set; }
		#endregion
		#region Constructors
		public Node()
		{
			NumDocs = 1;
		}

		public Node(Node previous, string nodeText)
		{
			NodeID = Guid.NewGuid();
			Parent = previous.Parent;
			Previous = previous;
			previous.Next = this;
			NodeText = nodeText;
			Text = (previous.Parent.Text != "/") ? previous.Parent.Text + " " + nodeText : nodeText;
			Depth = Previous.Parent.Depth + 1;
			NumDocs = 1;
			Node0 = new Node();
			Node0.Parent = this;
			Node0.NodeText = GST.SUFFIX_MARK;
			Node0.Text = Text;
			Node0.Depth = Depth;
		}
		#endregion
	}

}
